package recom;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import bean.TestCase;

import Controller.MainController;

import weka.Weka;

//import weka.Weka;

/**
 * @ClassName: Recommendation
 * @Description: 闁跨喐鏋婚幏鐑芥晸閺傘倖瀚�
 * @author justin yang justin_yang2012@gmail.com
 * @date 2011-10-26 闁跨喐鏋婚幏鐑芥晸閺傘倖瀚�:43:55
 * 
 */
public class Recommendation implements Runnable{
	private static final double UN_SELECT_SYMBOL = 0.0;

	private MainController mainController = null;

	// private static Logger logger = Logger.getLogger(Recommendation.class
	// .getName());

	enum TYPE {
		ANP, AEP, ANF, AEF
	}

	private int funNumber = 0;

	/** 
	* @Fields clusterIndex : the first cluter used to recommedate testcases 
	*/ 
	private int clusterIndex = 0;
	/**
	 * @Fields prefixOfClusterNum :
	 *         闁跨喐鏋婚幏铚傜疀闁跨喐鏋婚幏宄板闁跨喐鏋婚幏鐑芥晸鐞涙鍤栭幏锟介柨鐔告灮閹风兘鏁撻弬銈嗗
	 *         闁跨喐鏋婚幏宄板絿闁跨喐鏋婚幏閿嬬毉鐠嬪鏁撻弬銈嗗
	 */
	private double therold = 1;

	private String prefixOfClusterNum = "cluster";

	private String arffFileURL = "./data/FlexProfile/binaryProfile/V2.arff";

//	private String bugHitMatriURL = "./data/flex bugHitMatrix/V2.txt";
	/**
	 * @Fields outputFile : 闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归柨鐔告灮閹风兘鏁撻弬銈嗗閻╊喖缍�
	 */
	private String outputFile = "E://test.txt";

	private String valueFile = "E://value.txt";
	// /**
	// * @Fields priority : 闁跨喐鏋婚幏鐑芥晸閺傘倖瀚瑰В蹇涙晸閺傘倖瀚归柨鐔告灮閹风兘鏁撻弬銈嗗闁跨喐鏋婚幏閿嬫綀閸婏拷
	// */
	// private List<Double> priority;

	/**
	 * @Fields clusterList : 闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归弬婊堟晸閿燂拷
	 */
	private List<List<Integer>> clusterList = new ArrayList<List<Integer>>();

	/**
	 * @Fields testCaseList : total testCase list
	 */
	private List<List<Double>> testCaseList = new ArrayList<List<Double>>();

	/** 
	* @Fields recmmendedTCList : recommended test case 
	*/ 
//	List<List<Double>> recmmendedTCList = new ArrayList<List<Double>>();
	/**
	 * @Fields bugHitMatrixList :
	 *         闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归柨鐔稿复閿斿吋瀚归柨鐔告灮閹风兘鏁撻弬銈嗗闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归懞妤呮晸鐟欙絽鍩岄柨鐔告灮閹风
	 *         ug闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归柨鐕傛嫹
	 */
//	private List<String> bugHitMatrixList = new ArrayList<String>();

	/**
	 * @Fields confidenceOfFuncs :
	 *         濮ｅ繘鏁撻弬銈嗗闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归柨鐔告灮閹峰嘲绨查柨鐔告灮閹风onfidence閸婏拷
	 */
	private List<Double> confidenceOfFuncs = new ArrayList<Double>();

	/**
	 * @Fields valuesOfFunc : 闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归柨鐔告灮閹峰嘲缍嶅В蹇涙晸閺傘倖瀚归柨鐔告灮閹风兘鏁撻弬銈嗗
	 *         闁跨喐鏋婚幏绌塶p闁跨喐鏋婚幏锟絘ep闁跨喐鏋婚幏绌塶f闁跨喐鏋婚幏锟絘ef闁跨喐鏋婚幏宄帮拷
	 */
	private List<int[]> valuesOfFunc = new ArrayList<int[]>();

	/**
	 * @Fields clusterNumber : 闁跨喐鏋婚幏鐑筋湜闁跨喐鏋婚幏鐑芥晸閿燂拷
	 */
	private int clusterNumber = 50;
	/**
	 * @Fields suspiciousFuncList: 閸欘垳鏋掗崙鑺ユ殶閸掓銆�
	 */
	List<Integer> suspiciousFuncList = new ArrayList<Integer>();

	/**
	 * @Fields suspiciousnessOfTestCase: 濞村鐦悽銊ょ伐閻ㄥ嫬褰查悿鎴濓拷
	 */
	List<Double> suspiciousnessOfTestCase = new ArrayList<Double>();
	
	private boolean canGoOn = false;

//	 private double confidenceThreshold = 1.0;
	public void addClusterIndex() {
		clusterIndex++;

	}

	public int getClusterIndex() {
		return clusterIndex;
	}

	public boolean isCanGoOn() {
		return canGoOn;
	}

	public void setCanGoOn(boolean canGoOn) {
		this.canGoOn = canGoOn;
	}

	public Recommendation(MainController controller) {
		this.mainController = controller;
	}

//	 public double getConfidenceThreshold() {
//	 return confidenceThreshold;
//	 }
//	
//	 public void setConfidenceThreshold(double confidenceThreshold) {
//	 this.confidenceThreshold = confidenceThreshold;
//	 }

	public List<List<Integer>> getClusterList() {
		return clusterList;
	}

	public int getClusterNumber() {
		return clusterNumber;
	}

	public void setClusterNumber(int clusterNumber) {
		this.clusterNumber = clusterNumber;
	}

	public boolean setArffFile(String fileUrl) {
		File file = new File(fileUrl);
		if (file.exists()) {
			arffFileURL = fileUrl;
			return true;
		} else {
			return false;
		}
	}

	@Override
	public void run() {
		cluster();
		funNumber = calculateFunctionNumber();
		initialize(funNumber);
		readData();
		iterator();
		outputValue(valueFile);
		System.out.print("finish");
	}

	private void cluster() {
		Weka weka = new Weka(clusterNumber);
		weka.cluster(arffFileURL, outputFile);
	}

	private void initialize(int funcNum) {
//		readBugHitMatrix(bugHitMatriURL);

		// priority = new ArrayList<Double>(funcNum);
		// for (int i = 0; i < funcNum; i++) {
		// priority.add(new Double(1));
		// }

		for (int i = 0; i < clusterNumber; i++) {
			clusterList.add(new ArrayList<Integer>());
		}

		for (int i = 0; i < funcNum; i++) {
			int[] defaultValues = { 0, 0, 0, 0 };
			valuesOfFunc.add(defaultValues);
		}

		for (int i = 0; i < funcNum; i++) {
			confidenceOfFuncs.add(0.0);
		}

		for (int j = 0; j < funcNum; j++) {
			// if(confidenceOfFuncs.get(j) < therold){
			suspiciousFuncList.add(j);
			// }
		}
		// System.out.println("ssuspiciousFuncList" + suspiciousFuncList);
		// System.out.println(suspiciousFuncList.get(9));
	}

	private int calculateFunctionNumber() {
		int number = 0;
		try {
			BufferedReader bfReader = new BufferedReader(new FileReader(
					new File(outputFile)));
			String data = null;
			while ((data = bfReader.readLine()) != null) {
				if (data.startsWith("@attribute func")) {
					number++;
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return number;
	}

	/**
	 * @Title: readData
	 * @Description: 闁跨喐鏋婚幏宄板絿arff闁跨喍鑼庣涵閿嬪闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归柨鐔告灮閹烽攱鐦￠柨鐔告灮閹风兘鏁撻弬銈嗗
	 *               闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归柨鐔告灮閹风兘鏁撻弬銈嗗
	 *               闁跨喐鏋婚幏鐑芥晸閹恒儻璁ｉ幏鐑芥晸閺傘倖瀚归柨鐔告灮閹风兘鏁撻弬銈嗗
	 * @param dataFile
	 *            arff闁跨喍鑼庣涵閿嬪
	 * @return void
	 * @throws
	 */
	private boolean readData() {
		boolean result = false;
		try {
			BufferedReader bfReader = new BufferedReader(new FileReader(
					new File(outputFile)));
			String data = null;
			int indexOfTestcase = 0;
			while ((data = bfReader.readLine()) != null) {
				if (data.startsWith("@") || data.equals("")) {
					continue;
				}
				int clusterNum = Integer.parseInt(data.substring(data
						.indexOf(prefixOfClusterNum)
						+ prefixOfClusterNum.length()));
				
				String[] metaDatas = data.substring(data.indexOf(",") + 1,
						data.indexOf(prefixOfClusterNum) - 1).split(",");
				List<Double> testCase = new ArrayList<Double>();
				for(String metaData : metaDatas) {
					double value = Double.parseDouble(metaData);
					testCase.add(value);
				}
				testCaseList.add(testCase);
				
				List<Integer> cluster = clusterList.get(clusterNum);
				cluster.add(indexOfTestcase);
				indexOfTestcase += 1;
				suspiciousnessOfTestCase.add(0.0);
				result = true;
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

//	private void readBugHitMatrix(String dataFile) {
//		try {
//			BufferedReader bfReader = new BufferedReader(new FileReader(
//					new File(dataFile)));
//			String data = null;
//			while ((data = bfReader.readLine()) != null) {
//				data = data.replaceAll(",", "");
//				bugHitMatrixList.add(data);
//			}
//		} catch (FileNotFoundException e) {
//			e.printStackTrace();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//	}

	private void iterator() {
		// System.out.println(suspiciousnessOfTestCase);
		// System.out.println(suspiciousFuncList);
		for (int i = 0; i < clusterNumber; i++) {
			List<Integer> cluster = clusterList.get(i);
			System.out.println("cluster " + i + "before remove");
			System.out.println(cluster);
			do {
				int index = selectTheMostSuspiciousCase(cluster);

				if (-1 != index) {
					int indexOfTestCase = cluster.get(index);
					System.out.println("recommend: " + indexOfTestCase);
					List<Double> testCase = testCaseList.get(indexOfTestCase);
					// System.out.println("recommend: " + testCase);
					List<List<Double>> recommendedTCList = new ArrayList<List<Double>>();
					recommendedTCList.add(testCase);

					List<TestCase> testCaseList = new ArrayList<TestCase>();
					TestCase tc = new TestCase(indexOfTestCase);
					testCaseList.add(tc);
					mainController.showRecommendTestCase(testCaseList);
					// String bugHitMatrix =
					// bugHitMatrixList.get(indexOfTestCase);

					// boolean failure = checkTestcaseResult(bugHitMatrix);

					while(true) {
						if(!canGoOn) {
							try {
								Thread.sleep(1);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}else {
							canGoOn = false;
							break;
						}
					}
					List<Integer> successList = mainController.getResultsOfTestCases();

					List<Boolean> resultList = new ArrayList<Boolean>();

					for (int tmpI = 0; tmpI < testCaseList.size(); tmpI++) {
						if (successList.contains(tmpI)) {
							resultList.add(true);
						} else {
							resultList.add(false);
						}
					}
					boolean failure = resultList.get(0);
					accumulate(testCase, failure);
					updateConfidenceOfFuncs(testCase, failure);
				}
				cluster.remove(index);
				System.out.println("cluster " + i + "after remove");
				System.out.println(cluster);
			} while (hasSuspiciousTestCase(cluster));
		}
	}

//	private void recommend(int cIndex) {
//		if (cIndex < clusterNumber) {
//			List<Integer> cluster = clusterList.get(cIndex);
//			System.out.println("cluster " + cIndex + "before remove");
//			System.out.println(cluster);
//			
//			int index = selectTheMostSuspiciousCase(cluster);
//
//			if (-1 != index) {
//				int indexOfTestCase = cluster.get(index);
//				System.out.println("recommend: " + indexOfTestCase);
//				List<Double> testCase = testCaseList.get(indexOfTestCase);
//				// System.out.println("recommend: " + testCase);
//				recmmendedTCList.add(testCase);
//
//				List<TestCase> testCaseList = new ArrayList<TestCase>();
//				TestCase tc = new TestCase(indexOfTestCase);
//				testCaseList.add(tc);
//				mainController.showRecommendTestCase(testCaseList);
//
//				cluster.remove(index);
//				System.out.println("cluster " + cIndex + "after remove");
//				System.out.println(cluster);
//			}
//		}
//	}
//
//	public void updateTestCasesResult() {
//		List<Integer> successList = mainController.getResultsOfTestCases();
//
//		List<Boolean> resultList = new ArrayList<Boolean>();
//
//		for (int tmpI = 0; tmpI < testCaseList.size(); tmpI++) {
//			if (successList.contains(tmpI)) {
//				resultList.add(true);
//			} else {
//				resultList.add(false);
//			}
//		}
//		boolean failure = resultList.get(0);
//
//		List<Double> testCase = recmmendedTCList.get(0);
//		accumulate(testCase, failure);
//		updateConfidenceOfFuncs(testCase, failure);
//		
//		if(hasSuspiciousTestCase(clusterList.get(clusterIndex))) {
//			recommend(clusterIndex);
//		}else {
//			recommend(clusterIndex++);
//		}
//	}

	public boolean hasSuspiciousTestCase(List<Integer> l) {
		// System.out.println("ss" + suspiciousnessOfTestCase);
		for (int i : l) {
			if (suspiciousnessOfTestCase.get(i) > 0) {
				return true;
			}
		}
		return false;
	}

//	public boolean checkTestcaseResult(String bugHitMatrix) {
//		boolean failure = bugHitMatrix.contains("1");
//		return failure;
//	}

	public void updateConfidenceOfFuncs(List<Double> testCase, boolean failure) {
		suspiciousFuncList.removeAll(suspiciousFuncList);
		// System.out.println(suspiciousFuncList);
		int value = 0;
		if (failure) {
			value = -1;
		} else {
			value = 1;
		}

		for (int i = 0; i < testCase.size(); i++) {
			double select = testCase.get(i);
			if (UN_SELECT_SYMBOL != select) {
				double confidence = confidenceOfFuncs.get(i) + value;
				confidenceOfFuncs.set(i, confidence);
				// construct suspicious function list
				if (confidence < therold) {
					suspiciousFuncList.add(i);
				}
			}
		}
		// System.out.println(suspiciousFuncList);
		// System.out.println("suspicious function");
		// for (int i : suspiciousFuncList) {
		// System.out.print(i + ",");
		// }

	}

	// construct suspicious function list

	public void accumulate(List<Double> testCase, boolean failure) {
		int length = testCase.size();
		int v = 0;
		int[] values;
		double c;
		for (int i = 0; i < length; i++) {
			c = testCase.get(i);
			values = valuesOfFunc.get(i);
			if (failure && UN_SELECT_SYMBOL != c) {
				v = TYPE.AEF.ordinal();
			} else if (failure && UN_SELECT_SYMBOL == c) {
				v = TYPE.ANF.ordinal();
			} else if (!failure && UN_SELECT_SYMBOL != c) {
				v = TYPE.AEP.ordinal();
			} else if (!failure && UN_SELECT_SYMBOL == c) {
				v = TYPE.ANP.ordinal();
			}
			values[v] += 1;
		}
	}

	// private boolean isFinish(int indexOfCluster) {
	// boolean allSurpassCT = true;
	// Iterator<Double> it = suspicionOfFunctions.iterator();
	// while(it.hasNext()) {
	// Double sus = (Double)it.next();
	// if(sus < confidenceThreshold) {
	// allSurpassCT = false;
	// break;
	// }
	// }
	// return false;
	// }
	/**
	 * @Title: selectTheMostSuspiciousCase
	 * @Description: 闁跨喐鏋婚幏鐑斤拷闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归柨鐔告灮閹风兘鏁撻弬銈嗗閺嶏繝鏁撻弬銈嗗闁跨喐鏋婚幏鐑芥晸鐞涙顣
	 *               幏鐑芥晸閺傘倖瀚归悧鐔兼晸閺傘倖瀚归柨鐔告灮閹风兘鏁撻弬銈嗗闁跨噦鎷�
	 * @param 闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归柨锟
	 *            � * 閿燂拷
	 * @return int
	 *         闁跨喐鏋婚幏鐑斤拷闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归柨鐔惰寧鐠囇勫闁跨喐鏋婚幏鐑芥晸閺傘倖瀚归柨鐔告灮閹风兘鏁撻弬銈嗗閸椼倝鏁撻敓锟
	 *         � * @throws
	 */

	public int selectTheMostSuspiciousCase(List<Integer> TestcaseList) {

		// System.out.println();
		if (null == TestcaseList || TestcaseList.size() == 0) {
			return -1;
		}

		double suspiciousness = 0;
		int indexOfMostSuspiciousCase = 0;

		int currentIndex = 0;
		Iterator<Integer> it = TestcaseList.iterator();
		while (it.hasNext()) {
			int indexOfTestCase = (Integer) it.next();
			// System.out.println("indexofTestCase" + indexOfTestCase);

			List<Double> testCase = testCaseList.get(indexOfTestCase);
			// System.out.println("suspiciousFuncList" + suspiciousFuncList);
			double confidence = getConfidenceOfTestCase(testCase,
					suspiciousFuncList);
			suspiciousnessOfTestCase.set(indexOfTestCase, confidence);
			// System.out.println("confidence" + confidence);

			if (confidence > suspiciousness) {
				suspiciousness = confidence;
				indexOfMostSuspiciousCase = currentIndex;
			}
			currentIndex++;
		}
		// System.out.println("suspiciousness" + suspiciousnessOfTestCase);
		// int count = 0;
		/*
		 * for(double d: suspiciousnessOfTestCase){ if(d != 0.0){ count++; }
		 * System.out.println("count" + count);
		 * 
		 * }
		 */
		System.out.println("indexOfMostSuspiciousCase "
				+ indexOfMostSuspiciousCase);
		return indexOfMostSuspiciousCase;
	}

	private double getConfidenceOfTestCase(List<Double> testCase,
			List<Integer> confidences) {
		// System.out.println("testCase" + testCase);
		/*
		 * if (testCase.length() != confidences.size()) { return 0; }
		 */

		double confidence = 0;
		int length = testCase.size();
		for (int i = 0; i < length; i++) {
			double select = testCase.get(i);
			if (UN_SELECT_SYMBOL != select) {
				if (confidences.contains(i)) {
					confidence++;
				}
			}

		}

		// System.out.println("confidence " + confidence);
		return confidence;
	}

	private void outputValue(String filename) {
		try {
			PrintWriter writer = new PrintWriter(new FileWriter(filename));
			writer.println("TC\tAEP\tAEF\tANP\tANF");
			for (int i = 0; i < valuesOfFunc.size(); i++) {
				int[] values = valuesOfFunc.get(i);
				writer.println(i + "\t" + values[TYPE.AEP.ordinal()] + "\t"
						+ values[TYPE.AEF.ordinal()] + "\t"
						+ values[TYPE.ANP.ordinal()] + "\t"
						+ values[TYPE.ANF.ordinal()]);
			}
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
